package com.zqq.demo.camera1;

import android.hardware.Camera;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import androidx.appcompat.app.AppCompatActivity;

import com.zqq.base.utils.LogUtils;
import com.zqq.demo.R;
import com.zqq.demo.utils.FileUtils;

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;

public class CameraPreviewActivity extends AppCompatActivity implements SurfaceHolder.Callback, Camera.PreviewCallback {
    private Camera mCamera;
    private SurfaceView mSurfaceView;
    private SurfaceHolder mHolder;
    private int width = 1280;
    private int height = 720;

    private static final String MIME_TYPE = "video/avc";
    private static final int BIT_RATE = 4000000;
    private static final int FRAME_RATE = 30;
    private MediaCodec mEncoder;
    private String filePathH264;
    private String filePathMp4;
    private H264ToMp4Converter h264ToMp4Converter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera_preview);
        
        mSurfaceView = findViewById(R.id.surface_view);
        mHolder = mSurfaceView.getHolder();
        mHolder.addCallback(this);

        File file = new File( getExternalCacheDir()+"/videos");
        if(!file.exists()){
            file.mkdirs();
        }
        File h264 = new File(file,System.currentTimeMillis()+".h264");
        if(!h264.exists()){
            try {
                h264.createNewFile();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        filePathH264 = h264.getPath();

        File mp4 = new File(file,System.currentTimeMillis()+".mp4");
        if(!mp4.exists()){
            try {
                mp4.createNewFile();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        filePathMp4 = mp4.getPath();

        LogUtils.i("bl_camera","filePathH264>>"+filePathH264);
        LogUtils.i("bl_camera","filePathMp4>>"+filePathMp4);

        initMediaCodec();
    }

    private void initMediaCodec() {

        h264ToMp4Converter = new H264ToMp4Converter();

        try {
            mEncoder = MediaCodec.createEncoderByType(MIME_TYPE);
            MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, width, height);
            format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
            format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
            format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
                    MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);
            format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
            mEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

            // 关键参数：必须设置CSD数据
            byte[] header_sps = {0,0,0,1,103,100,0,31,(byte)172,(byte)217,64,(byte)160,(byte)5,(byte)189};
            byte[] header_pps = {0,0,0,1,104,(byte)238,60,(byte)128};
            ByteBuffer sps = ByteBuffer.wrap(header_sps);
            ByteBuffer pps = ByteBuffer.wrap(header_pps);
            format.setByteBuffer("csd-0", sps);
            format.setByteBuffer("csd-1", pps);
            h264ToMp4Converter.init(filePathMp4,format,width,height);

            mEncoder.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        try {
            mCamera = Camera.open();
            Camera.Parameters params = mCamera.getParameters();
            params.setPreviewSize(width, height);
            mCamera.setDisplayOrientation(90);
            params.setRotation(90);
            mCamera.setParameters(params);
            mCamera.setPreviewDisplay(holder);
            mCamera.setPreviewCallback(this);
            mCamera.startPreview();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        if (mHolder.getSurface() == null) return;
        
        try {
            mCamera.stopPreview();
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

        if(h264ToMp4Converter != null){
            h264ToMp4Converter.release();
        }


        if (mEncoder != null) {
            mEncoder.stop();
            mEncoder.release();
            mEncoder = null;
        }


        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }

    }

    @Override
    public void onPreviewFrame(byte[] bytes, Camera camera) {
        LogUtils.i("bl_camera","bytes>>"+bytes);
        ecode(bytes);
        camera.addCallbackBuffer(bytes);
    }
    byte[] mPpsSps = new byte[0];
    private void ecode(byte[] data) {
        try {
            ByteBuffer[] inputBuffers = mEncoder.getInputBuffers();
            int inputBufferIndex = mEncoder.dequeueInputBuffer(-1);
            if (inputBufferIndex >= 0) {
                ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
                inputBuffer.clear();
                inputBuffer.put(data);
                mEncoder.queueInputBuffer(inputBufferIndex, 0, data.length,
                        System.nanoTime() / 1000, 0);
            }

            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
            int outputBufferIndex = mEncoder.dequeueOutputBuffer(bufferInfo, 0);
            while (outputBufferIndex >= 0) {
                ByteBuffer outputBuffer = mEncoder.getOutputBuffer(outputBufferIndex);
                byte[] outData = new byte[bufferInfo.size];
                outputBuffer.get(outData);
                // 这里可以处理编码后的H264数据
//                FileUtils.writeFile(filePathH264,outData);

//                boolean sync = false;
//                if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {// sps
//                    sync = (bufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0;
//                    if (!sync) {
//                        byte[] temp = new byte[bufferInfo.size];
//                        outputBuffer.get(temp);
//                        mPpsSps = temp;
//                    } else {
//                        mPpsSps = new byte[0];
//                    }
//                }
//                boolean isKeyFrame = (outData[4] & 0x1F) == 0x05;
                h264ToMp4Converter.writeSampleData(outData,bufferInfo.presentationTimeUs);
                mEncoder.releaseOutputBuffer(outputBufferIndex, false);
                outputBufferIndex = mEncoder.dequeueOutputBuffer(bufferInfo, 0);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
